package moon.excel.util;

import java.lang.reflect.Array;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.beanutils.ConvertUtilsBean;
import org.apache.commons.beanutils.Converter;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.poi.ss.usermodel.DateUtil;

import moon.excel.bean.ExcelFun;
import moon.excel.constants.ExcelConstants;
import moon.util.StringUtils;

public class ExcelUtil {
	public static ConvertUtilsBean convertUtilsBean = new ConvertUtilsBean();

	static {
		convertUtilsBean.register(new Converter() {

			@SuppressWarnings("unchecked")
			@Override
			public <T> T convert(Class<T> type, Object value) {
				if (value != null) {
					return (T) DateUtil.getJavaDate(Double.parseDouble(value.toString()));
				}
				return null;
			}
		}, Date.class);
		convertUtilsBean.register(new Converter() {

			@SuppressWarnings("unchecked")
			@Override
			public <T> T convert(Class<T> type, Object value) {
				if (value != null) {
					String src = value.toString();
					if (src.matches("\\d+(\\.\\d+)?")) {
						BigDecimal bigDecimal = new BigDecimal(value.toString());
						return (T) new Integer(bigDecimal.intValue());
					}
				}
				return null;
			}
		}, Integer.class);
	}

	/**
	 * 转换成指定类型的对象
	 * 
	 * @param o
	 * @param c
	 * @return
	 */
	public static Object convert(Object o, Class<?> c) {
		if (o == null) {
			return "";
		}
		return convertUtilsBean.convert(o.toString(), c);
	}

	/** 数组是否为空 */
	public static boolean isEmpty(Object[] os) {
		boolean isEmpty = true;
		if (ArrayUtils.isNotEmpty(os)) {
			for (Object o : os) {
				if (o != null && !o.toString().equals("")) {
					isEmpty = false;
					break;
				}
			}
		}
		return isEmpty;
	}

	/**
	 * 以忽略方法大小写的方式调用对象的方法。
	 * 
	 * @param o
	 *            被调用的对象
	 * @param methodName
	 *            方法名字
	 * @param args
	 *            参数列表
	 * @return 如果该方法不存在或返回类型为void,则返回null,否则返回调用方法后的结果。
	 */
	public static Object invokeMethod(Object o, String methodName, Object... args) {
		try {
			Method[] ms = o.getClass().getMethods();
			Method.setAccessible(ms, true);
			for (Method m : ms) {
				if (m.getName().equalsIgnoreCase(methodName))
					return m.invoke(o, args);
			}
			return null;
		} catch (Exception e) {
			e.printStackTrace();
			throw new RuntimeException(e);
		}
	}

	/**
	 * 是否为空
	 * 
	 * @param o
	 * @return
	 */
	public static boolean isNotBlank(Object o) {
		if (o != null && StringUtils.isNotBlank(o.toString())) {
			return true;
		}
		return false;
	}

	/** 根据字母返回列数 */
	public static int getColumnIndex(String c) {
		// DCBA
		int res = ExcelConstants.columnMap.get(c.charAt(c.length() - 1));
		try {
			if (c.length() > 1) {
				for (int i = 1; i < c.length(); i++) {
					char r = c.charAt(i);
					res = res + new Double(Math.pow(26, c.length() - i)).intValue() * ExcelConstants.columnMap.get(r);
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return res;
	}

	/**
	 * 字母列转换为数字 Excel column index begin 1
	 * 
	 * @param columnIndex
	 * @return
	 */
	public static String excelColIndexToStr(int columnIndex) {
		if (columnIndex <= 0) {
			return null;
		}
		String columnStr = "";
		columnIndex--;
		do {
			if (columnStr.length() > 0) {
				columnIndex--;
			}
			columnStr = ((char) (columnIndex % 26 + (int) 'A')) + columnStr;
			columnIndex = (int) ((columnIndex - columnIndex % 26) / 26);
		} while (columnIndex > 0);
		return columnStr;
	}

	/** 对象调用链 a.b.c 不支持集合属性 */
	public static Object invokeGetMethod(Object o, String attrName) {
		Object r = null;
		try {
			if (o != null && StringUtils.isNotBlank(attrName)) {
				if (!attrName.contains(".")) {
					r = ExcelUtil.invokeMethod(o, "get" + attrName);
				} else {
					String[] attrNames = attrName.split("\\.");
					r = invokeGetMethod(ExcelUtil.invokeMethod(o, "get" + attrNames[0]), attrName.substring(attrName.indexOf(".") + 1));
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}

		return r == null ? "" : r;
	}

	private static ExcelFun parseFun(String fun) {
		int lpair = fun.indexOf("(");
		String funparams = fun.substring(lpair + 1, fun.lastIndexOf(")"));
		String[] params = null;
		List<String> realparams = new ArrayList<String>();
		if (StringUtils.isNotBlank(funparams)) {
			StringBuilder paramsb = new StringBuilder();
			String[] funps = funparams.split(",");
			for (String p : funps) {
				if (p.matches(".*[\\(\\)]+.*")) {
					paramsb.append(p + ",");
				} else {
					if (paramsb.length() > 0) {
						realparams.add(paramsb.substring(0, paramsb.length() - 1));
						paramsb = new StringBuilder();
					}
					realparams.add(p);
				}

			}
		}
		if (!realparams.isEmpty()) {
			params = realparams.toArray(new String[] {});
		}
		String funbody = fun.substring(1, lpair);
		String[] funkv = funbody.split("\\.");
		return new ExcelFun(funkv[1], params, funkv[0]);
	}

	/**
	 * 支持以下属性命名方式 <br>
	 * 1.对象调用链 a.b.c的方式获取嵌套属性值 不支持集合属性 <br>
	 * 2.attr1{字符串}attr2 用{}中的字符串连接属性值 <br>
	 * 3.IAttrHandle 自定义处理属性值 <br>
	 * 4.函数以$开头，如$a.b(参数) <br>
	 * 默认date->com.liantuo.club.common.utils.DateUtils,number->com.liantuo.club. common.utils.NumberUtils。 可以调用addFunctions增加自定函数 <br>
	 * eg. <br>
	 * abc$date.formatDate(a.b.c,{yyyyMMdd}){连接}d.e.f &nbsp;&nbsp;结果:abc20120102链接def
	 */
	public static Object parseAttr(Object o, Object attrName, Map<String, Object> functions) {
		Object r = null;
		try {
			if (o != null && ExcelUtil.isNotBlank(attrName)) {
				String attrNameStr = attrName.toString().trim();
				r = parseComplexAttrEl(o, attrNameStr, functions);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}

		return r == null ? "" : r;
	}

	public static Object parseComplexAttrEl(Object o, String attr, Map<String, Object> functions) {
		Object r = null;
		List<String> parts = parseEl(attr);
		if (!attr.matches(".*[{}\\$].*")) {
			r = ExcelUtil.invokeGetMethod(o, attr);
		} else if (parts.size() == 1 && parts.get(0).startsWith("$")) {
			r = invokeFun(o, parts.get(0), functions);
		} else {
			StringBuilder sr = new StringBuilder();
			for (String part : parts) {
				if (part.startsWith("{")) {
					sr.append(part.substring(1, part.length() - 1));
				} else if (part.startsWith("$")) {
					sr.append(invokeFun(o, part, functions));
				} else {
					sr.append(ExcelUtil.invokeGetMethod(o, part));
				}
			}
			r = sr.toString();
		}
		return r;
	}

	/***
	 * 解析表达式
	 * 
	 * @param attrNameStr
	 * @return
	 */
	private static List<String> parseEl(String attrNameStr) {
		List<String> parts = new ArrayList<String>();
		SimpleMonocaseELParse elParse = new SimpleMonocaseELParse();
		elParse.register("text", '{', '}');
		elParse.register("fun", '$', ')');
		List<Map.Entry<String, String>> list = elParse.parse(attrNameStr);
		for (Map.Entry<String, String> e : list) {
			parts.add(e.getValue());
		}
		return parts;
	}

	/**
	 * 解析函数
	 * 
	 * @param o
	 * @param fun
	 * @param columnNames
	 * @return
	 */
	private static Object invokeFun(Object o, String fun, Map<String, Object> functions) {
		Object r = null;
		if (functions != null && !functions.isEmpty()) {
			Map<String, Object> map = parseMethod(fun, functions);
			String[] param = (String[]) map.get("params");
			Object src = map.get("obj");
			Object[] paramos = null;
			Method method = (Method) map.get("fun");
			Class<?>[] paramTypes = method.getParameterTypes();
			param = Arrays.copyOf(param, paramTypes.length);
			if (param != null) {
				paramos = new Object[param.length];
				for (int i = 0; i < param.length; i++) {
					paramos[i] = StringUtils.isNotBlank(param[i]) ? parseComplexAttrEl(o, param[i], functions) : param[i];
					if (paramTypes[i].isArray()) {
						Class<?> ctype = paramTypes[i].getComponentType();
						Object array = null;
						if (StringUtils.isNotBlank(param[i])) {
							array = Array.newInstance(ctype, 1);
							Array.set(array, 0, paramos[i]);
						} else {
							array = Array.newInstance(ctype, 0);
						}
						paramos[i] = array;
					} else if (paramos[i] instanceof String && !paramTypes[i].isAssignableFrom(String.class)) {
						paramos[i] = ExcelUtil.convert(paramos[i], paramTypes[i]);
					}
				}
			}
			method.setAccessible(true);
			try {
				r = method.invoke(src, paramos);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		return r == null ? "" : r;
	}

	private static Map<String, Object> parseMethod(String fun, Map<String, Object> functions) {
		Map<String, Object> map = new HashMap<String, Object>();
		ExcelFun efun = ExcelUtil.parseFun(fun);
		Object cla = functions.get(efun.getFunObjectName());
		if (cla == null) {
			throw new RuntimeException("没有与" + efun.getFunObjectName() + "对应的对象。");
		}
		map.put("obj", cla);
		Method des = null;
		for (Method m : cla.getClass().getMethods()) {
			if (m.getName().equals(efun.getFunName())) {
				des = m;
				break;
			}
		}
		if (des == null) {
			throw new RuntimeException("方法" + efun.getFunName() + "不存在。");
		}
		map.put("fun", des);
		map.put("params", efun.getFunParam());
		return map;
	}

}
